# coding: utf-8

from ala_classes import SyntaxTreeNode, Token, TokenType


class SyntaxTreeOptimizer(object):
    NON_COLLAPSABLE_PRODUCTIONS = ['start', 'print_expr', 'funcall_arguments', ]
    NON_DELETABLE_PRODUCTIONS = ['funcall_arguments', ]
    __changed = None

    tree = None

    def __init__(self, tree):
        self.__changed = False
        self.tree = tree

    # INTERFACE

    def run(self):
        self.__delete_nonproductive_tokens(self.tree)
        self.__changed = True
        while self.__changed:
            self.__changed = False
            self.__delete_empty_productions(self.tree)
            self.__collapse_single_productions(self.tree)
            self.__collapse_help_productions(self.tree)
        self.__hide_tokens_finally(self.tree)

    # PRIVATE METHODS

    def __delete_nonproductive_tokens(self, node):
        if type(node) == SyntaxTreeNode:
            new_parts = node.parts[:]
            for child in node.parts:
                if type(child) == SyntaxTreeNode:
                    self.__delete_nonproductive_tokens(child)
                elif type(child) == Token and not child.is_valuable():
                    new_parts.remove(child)
                    self.__changed = True
            node.parts = new_parts

    def __delete_empty_productions(self, node):
        if type(node) == SyntaxTreeNode:
            for child in node.parts:
                if self.__delete_empty_productions(child) and SyntaxTreeOptimizer.__is_deletable\
                            (child.production):
                    node.parts.remove(child)
                    self.__changed = True
            return len(node.parts) == 0
        return False

    def __collapse_single_productions(self, node):
        if type(node) == SyntaxTreeNode:
            for i in xrange(len(node.parts)):
                if (self.__collapse_single_productions(node.parts[i]) and
                        SyntaxTreeOptimizer.__is_collapsable(node.parts[i].production)):
                    node.parts[i].parts[0].parent = node
                    node.parts[i] = node.parts[i].parts[0]
                    self.__changed = True
            return len(node.parts) == 1
        return False

    def __collapse_help_productions(self, node):
        if type(node) == SyntaxTreeNode:
            new_parts = node.parts[:]
            index_shift = 0
            for i in xrange(len(node.parts)):
                if self.__collapse_help_productions(node.parts[i]):
                    del new_parts[i + index_shift]
                    for part in node.parts[i].parts:
                        new_parts.insert(i + index_shift, part)
                        part.parent = node
                        index_shift += 1
                    self.__changed = True
            node.parts = new_parts
            return node.production[-2:] == "__"
        return False

    def __hide_tokens_finally(self, node):
        if type(node) == SyntaxTreeNode:
            new_parts = node.parts[:]
            for i in xrange(len(node.parts)):
                child = node.parts[i]
                if type(child) == SyntaxTreeNode:
                    self.__hide_tokens_finally(child)
                elif type(child) == Token:
                    if child.needs_to_hide_finally():
                        new_parts.remove(child)
                        self.__changed = True
                    elif child.type == TokenType.COMMA:
                        if node.production != "ve_list_print" or i < len(node.parts) - 1:
                            new_parts.remove(child)

            node.parts = new_parts

    @staticmethod
    def __is_collapsable(production):
        return production not in SyntaxTreeOptimizer.NON_COLLAPSABLE_PRODUCTIONS

    @staticmethod
    def __is_deletable(production):
        return production not in SyntaxTreeOptimizer.NON_DELETABLE_PRODUCTIONS